package org.example.domain.order.service;


import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.example.domain.order.adapter.repository.IOrderRepository;
import org.example.domain.order.model.entity.DelayCancelOrderEntity;
import org.example.domain.order.model.entity.OrderEntity;
import org.example.domain.shop.model.entity.ProductEntity;
import org.example.domain.shop.service.IShopService;
import org.example.types.enums.ResponseCode;
import org.example.types.exception.AppException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.math.BigDecimal;
import java.util.Collections;

import static org.example.types.common.MQConstants.*;

/**
 * @author yinghuashen
 * @version 1.0
 * @description:
 * @date 2025/3/9 21:13
 */

@Slf4j
@Service
public class OrderService implements IOrderService {

    @Resource
    private IShopService shopService;

    @Resource
    private IOrderRepository orderRepository;

    @Resource
    private RocketMQTemplate rocketMQTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //下单lua脚本
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    //恢复库存lua脚本
    private static final DefaultRedisScript<Long> STOCK_ROLLBACK_SCRIPT;

    static {
        STOCK_ROLLBACK_SCRIPT = new DefaultRedisScript<>();
        STOCK_ROLLBACK_SCRIPT.setLocation(new ClassPathResource("stockRollback.lua"));
        STOCK_ROLLBACK_SCRIPT.setResultType(Long.class);
    }

    @Override
    // TODO 重构一版锁单逻辑，使用延时队列实现关闭订单
    public String lockOrder(String userId, String productId, Integer count){
        try {
            // 1. 查询商品信息
            ProductEntity productEntity = orderRepository.getProductById(productId);
            // 2. 计算优惠价格
            productEntity.setDiscountPrice(shopService.calculateDiscountPrice(productId, productEntity.getOriginalPrice()));

            // 1.2 TODO 唯一ID生成器
            String orderId = RandomUtil.randomNumbers(10);
            // 1.3 构造订单
            OrderEntity orderEntity = OrderEntity.builder().
                    orderId(orderId).userId(userId).
                    productId(productId).
                    productName(productEntity.getProductName()).
                    productAmount(count).
                    originalPrice(productEntity.getOriginalPrice()).
                    payPrice(productEntity.getDiscountPrice().multiply(new BigDecimal(count))).
                    status(OrderEntity.OrderStatus.PENDING_PAYMENT.getCode()).
                    build();

            // 2. RocketMQ 事务一致性消息
            Message<OrderEntity> message = MessageBuilder.withPayload(orderEntity).build();
            // 3. 发送half消息

//           超时重试范例
//            TransactionSendResult transactionSendResult = null;
//            for (int i = 0; i < 3; i++) { // 重试3次
//                try {
//                    transactionSendResult = rocketMQTemplate.sendMessageInTransaction(LOCK_ORDER_PRODUCER_GROUP,
//                            LOCK_ORDER_TOPIC + ":" + NEW_ORDER_TAG, message, orderEntity);
//                    break; // 成功就跳出循环
//                } catch (Exception e) {
//                    log.error("事务消息发送失败，尝试重试 {}/3 次", i + 1, e);
//                }
//            }

            TransactionSendResult transactionSendResult = rocketMQTemplate.
                    sendMessageInTransaction(LOCK_ORDER_PRODUCER_GROUP,
                            LOCK_ORDER_TOPIC+":"+NEW_ORDER_TAG,
                            message, orderEntity);

            // TODO 网络错误，一直无sendStatus，MQ是如何处理的？超时机制吗
            SendStatus sendStatus = transactionSendResult.getSendStatus();
            LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
            if(sendStatus == SendStatus.SEND_OK && localTransactionState == LocalTransactionState.COMMIT_MESSAGE){
                log.info("下单成功，事务状态为：{}", localTransactionState);
                return orderId;
            }
            // 出现错误，返回空订单号
            return "";
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    @Override
    // 第2版锁单逻辑
    public String lockOrder_v2(OrderEntity orderEntity) {
        // 1. TODO 唯一ID生成器
        String orderId = RandomUtil.randomNumbers(10);

        // 2. 扣减库存
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                orderEntity.getProductId(),
                orderEntity.getUserId()
        );

        // 3. 判断是否锁单成功
        if(result == null || result != 0){
            throw new AppException(ResponseCode.ORDER_LOCK_ERROR);
        }

        // 4. 扣减库存成功，开启事务订单落库
        // 4.1.1 查询商品信息
        ProductEntity productEntity = orderRepository.getProductByIdNow(orderEntity.getProductId());
        // 4.1.2 计算优惠价格
        productEntity.setDiscountPrice(shopService.calculateDiscountPrice(orderEntity.getProductId(), productEntity.getOriginalPrice()));
        // 4.1.3 构造订单
        orderEntity.setOrderId(orderId);
        orderEntity.setProductName(productEntity.getProductName());
        orderEntity.setOriginalPrice(productEntity.getOriginalPrice());
        orderEntity.setPayPrice(productEntity.getDiscountPrice().multiply(new BigDecimal(orderEntity.getProductAmount())));
        orderEntity.setStatus(OrderEntity.OrderStatus.PENDING_PAYMENT.getCode());
        // 4.1.4 落库
        boolean saveRes = orderRepository.saveOrder(orderEntity);
        if(!saveRes){
            // 执行缓存库存回滚
            stringRedisTemplate.execute(
                    STOCK_ROLLBACK_SCRIPT,
                    Collections.emptyList(),
                    orderEntity.getProductId(),
                    orderEntity.getUserId()
            );
            // 即使此处lua脚本执行失败，每5分钟刷新的定时任务会实现库存恢复
            throw new AppException(ResponseCode.ORDER_DB_INSERT_ERROR);
        }

        // 5. 开启延时任务，关闭订单
        DelayCancelOrderEntity delayCancelOrderEntity = DelayCancelOrderEntity.builder().
                orderId(orderId).
                userId(orderEntity.getUserId()).build();
        Message<DelayCancelOrderEntity> delayCancelOrderMessage = MessageBuilder.withPayload(delayCancelOrderEntity).build();

        rocketMQTemplate
                .asyncSend(DELAY_CANCEL_ORDER_TOPIC, delayCancelOrderMessage, new SendCallback() {
                            @Override
                            public void onSuccess(SendResult sendResult) {
                                if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                                    // 加入本地信息表
                                    orderRepository.saveDelayCancelOrderMsg(delayCancelOrderEntity);
                                }
                            }
                            @Override
                            public void onException(Throwable throwable) {
                                // 加入本地信息表
                                orderRepository.saveDelayCancelOrderMsg(delayCancelOrderEntity);
                            }
                        },
                        DELAY_CANCEL_ORDER_TIMEOUT, DELAY_LEVEL_30S);

        // 6. 返回订单号
        return orderId;
    }
}
